]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSMacOSX/Tests/Unit Tests/CNameRecordTest.m
mDNSResponder-1310.40.42.tar.gz
[apple/mdnsresponder.git] / mDNSMacOSX / Tests / Unit Tests / CNameRecordTest.m
1 /*
2 * Copyright (c) 2017-2019 Apple Inc. All rights reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "unittest_common.h"
18 #import <XCTest/XCTest.h>
19
20 struct UDPSocket_struct
21 {
22 mDNSIPPort port; // MUST BE FIRST FIELD -- mDNSCoreReceive expects every UDPSocket_struct to begin with mDNSIPPort port
23 };
24 typedef struct UDPSocket_struct UDPSocket;
25
26 // This client request was generated using the following command: "dns-sd -Q 123server.dotbennu.com. A".
27 uint8_t query_client_msgbuf[35] = {
28 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x32, 0x33, 0x73, 0x65, 0x72, 0x76, 0x65,
29 0x72, 0x2e, 0x64, 0x6f, 0x74, 0x62, 0x65, 0x6e, 0x6e, 0x75, 0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x00,
30 0x01, 0x00, 0x01
31 };
32
33 // This uDNS message is a canned response that was originally captured by wireshark.
34 uint8_t query_response_msgbuf[108] = {
35 0x69, 0x41, // transaction id
36 0x85, 0x80, // flags
37 0x00, 0x01, // 1 question for 123server.dotbennu.com. Addr
38 0x00, 0x02, // 2 anwsers: 123server.dotbennu.com. CNAME test212.dotbennu.com., test212.dotbennu.com. Addr 10.100.0.1,
39 0x00, 0x01, // 1 authorities anwser: dotbennu.com. NS cardinal2.apple.com.
40 0x00, 0x00, 0x09, 0x31, 0x32, 0x33,
41 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x08, 0x64, 0x6f, 0x74, 0x62, 0x65, 0x6e, 0x6e, 0x75, 0x03,
42 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01, 0xc0, 0x0c, 0x00, 0x05, 0x00, 0x01, 0x00, 0x00,
43 0x02, 0x56, 0x00, 0x0a, 0x07, 0x74, 0x65, 0x73, 0x74, 0x32, 0x31, 0x32, 0xc0, 0x16, 0xc0, 0x34,
44 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x04, 0x0a, 0x64, 0x00, 0x01, 0xc0, 0x16,
45 0x00, 0x02, 0x00, 0x01, 0x00, 0x01, 0x51, 0x80, 0x00, 0x12, 0x09, 0x63, 0x61, 0x72, 0x64, 0x69,
46 0x6e, 0x61, 0x6c, 0x32, 0x05, 0x61, 0x70, 0x70, 0x6c, 0x65, 0xc0, 0x1f
47 };
48
49 // Variables associated with contents of the above uDNS message
50 #define uDNS_TargetQID 16745
51 char udns_original_domainname_cstr[] = "123server.dotbennu.com.";
52 char udns_cname_domainname_cstr[] = "test212.dotbennu.com.";
53 static const mDNSv4Addr dns_response_ipv4 = {{ 10, 100, 0, 1 }};
54
55 @interface CNameRecordTest : XCTestCase
56 {
57 UDPSocket* local_socket;
58 request_state* client_request_message;}
59 @end
60
61 @implementation CNameRecordTest
62
63 // The InitThisUnitTest() initializes the mDNSResponder environment as well as
64 // a DNSServer. It also allocates memory for a local_socket and client request.
65 // Note: This unit test does not send packets on the wire and it does not open sockets.
66 - (void)setUp
67 {
68 // Init unit test environment and verify no error occurred.
69 mStatus result = init_mdns_environment(mDNStrue);
70 XCTAssertEqual(result, mStatus_NoError);
71
72 // Add one DNS server and verify it was added.
73 AddDNSServer_ut();
74 XCTAssertEqual(CountOfUnicastDNSServers(&mDNSStorage), 1);
75
76 // Create memory for a socket that is never used or opened.
77 local_socket = (UDPSocket *) mDNSPlatformMemAllocateClear(sizeof(*local_socket));
78
79 // Create memory for a request that is used to make this unit test's client request.
80 client_request_message = calloc(1, sizeof(request_state));
81 }
82
83 - (void)tearDown
84 {
85 mDNS *m = &mDNSStorage;
86 request_state* req = client_request_message;
87 DNSServer *ptr, **p = &m->DNSServers;
88
89 while (req->replies)
90 {
91 reply_state *reply = req->replies;
92 req->replies = req->replies->next;
93 mDNSPlatformMemFree(reply);
94 }
95 mDNSPlatformMemFree(req);
96
97 mDNSPlatformMemFree(local_socket);
98
99 while (*p)
100 {
101 ptr = *p;
102 *p = (*p)->next;
103 LogInfo("FinalizeUnitTest: Deleting server %p %#a:%d (%##s)", ptr, &ptr->addr, mDNSVal16(ptr->port), ptr->domain.c);
104 mDNSPlatformMemFree(ptr);
105 }
106 }
107
108 - (void)testCNameRecordTestSeries
109 {
110 [self _startClientQueryRequest];
111 [self _populateCacheWithClientResponseRecords];
112 [self _simulateNetworkChangeAndVerify];
113 }
114
115 // This test simulates a uds client request by setting up a client request and then
116 // calling mDNSResponder's handle_client_request. The handle_client_request function
117 // processes the request and starts a query. This unit test verifies
118 // the client request and query were setup as expected. This unit test also calls
119 // mDNS_execute which determines the cache does not contain the new question's
120 // answer.
121 - (void)_startClientQueryRequest
122 {
123 mDNS *const m = &mDNSStorage;
124 request_state* req = client_request_message;
125 char *msgptr = (char *)query_client_msgbuf;
126 size_t msgsz = sizeof(query_client_msgbuf);
127 mDNSs32 min_size = sizeof(DNSServiceFlags) + sizeof(mDNSu32) + 4;
128 DNSQuestion *q;
129 mStatus err = mStatus_NoError;
130 char qname_cstr[MAX_ESCAPED_DOMAIN_NAME];
131
132 // Process the unit test's client request
133 start_client_request(req, msgptr, msgsz, query_request, local_socket);
134 XCTAssertEqual(err, mStatus_NoError);
135
136 // Verify the request fields were set as expected
137 XCTAssertNil((__bridge id)req->next);
138 XCTAssertNil((__bridge id)req->primary);
139 XCTAssertEqual(req->sd, client_req_sd);
140 XCTAssertEqual(req->process_id, client_req_process_id);
141 XCTAssertFalse(strcmp(req->pid_name, client_req_pid_name));
142 XCTAssertEqual(req->validUUID, mDNSfalse);
143 XCTAssertEqual(req->errsd, 0);
144 XCTAssertEqual(req->uid, client_req_uid);
145 XCTAssertEqual(req->ts, t_complete);
146 XCTAssertGreaterThan((mDNSs32)req->data_bytes, min_size);
147 XCTAssertEqual(req->msgend, msgptr+msgsz);
148 XCTAssertNil((__bridge id)(void*)req->msgbuf);
149 XCTAssertEqual(req->hdr.version, VERSION);
150 XCTAssertNil((__bridge id)req->replies);
151 XCTAssertNotEqual(req->terminate, (req_termination_fn)0);
152 XCTAssertEqual(req->flags, kDNSServiceFlagsReturnIntermediates);
153 XCTAssertEqual(req->interfaceIndex, kDNSServiceInterfaceIndexAny);
154
155 // Verify the query fields were set as expected
156 q = &req->u.queryrecord.op.q;
157 XCTAssertNotEqual(q, (DNSQuestion *)mDNSNULL);
158 XCTAssertEqual(q, m->Questions);
159 XCTAssertEqual(q, m->NewQuestions);
160 XCTAssertEqual(q->SuppressUnusable, mDNSfalse);
161 XCTAssertEqual(q->ReturnIntermed, mDNStrue);
162 XCTAssertEqual(q->Suppressed, mDNSfalse);
163
164 ConvertDomainNameToCString(&q->qname, qname_cstr);
165 XCTAssertFalse(strcmp(qname_cstr, udns_original_domainname_cstr));
166 XCTAssertEqual(q->qnamehash, DomainNameHashValue(&q->qname));
167
168 XCTAssertEqual(q->InterfaceID, mDNSInterface_Any);
169 XCTAssertEqual(q->flags, req->flags);
170 XCTAssertEqual(q->qtype, 1);
171 XCTAssertEqual(q->qclass, 1);
172 XCTAssertEqual(q->LongLived, 0);
173 XCTAssertEqual(q->ExpectUnique, mDNSfalse);
174 XCTAssertEqual(q->ForceMCast, 0);
175 XCTAssertEqual(q->TimeoutQuestion, 0);
176 XCTAssertEqual(q->WakeOnResolve, 0);
177 XCTAssertEqual(q->UseBackgroundTraffic, 0);
178 XCTAssertEqual(q->ProxyQuestion, 0);
179 XCTAssertNotEqual((void*)q->QuestionCallback, (void*)mDNSNULL);
180 XCTAssertEqual(q->AppendSearchDomains, 0);
181 XCTAssertNil((__bridge id)q->DuplicateOf);
182
183 // Call mDNS_Execute to see if the new question, q, has an answer in the cache.
184 // It won't be yet because the cache is empty.
185 m->NextScheduledEvent = mDNS_TimeNow_NoLock(m);
186 mDNS_Execute(m);
187
188 // Verify mDNS_Execute processed the new question.
189 XCTAssertNil((__bridge id)m->NewQuestions);
190
191 // Verify the cache is empty and the request got no reply.
192 XCTAssertEqual(m->rrcache_totalused, 0);
193 XCTAssertNil((__bridge id)req->replies);
194 }
195
196 // This unit test receives a canned uDNS response message by calling the mDNSCoreReceive() function.
197 // It then verifies cache entries were added for the CNAME and A records that were contained in the
198 // answers of the canned response, query_response_msgbuf. This unit test also verifies that
199 // 2 add events were generated for the client.
200 - (void)_populateCacheWithClientResponseRecords
201 {
202 mDNS *const m = &mDNSStorage;
203 DNSMessage *msgptr = (DNSMessage *)query_response_msgbuf;
204 size_t msgsz = sizeof(query_response_msgbuf);
205 struct reply_state *reply;
206 request_state* req = client_request_message;
207 DNSQuestion *q = &req->u.queryrecord.op.q;
208 const char *data;
209 const char *end;
210 char name[kDNSServiceMaxDomainName];
211 uint16_t rrtype, rrclass, rdlen;
212 const char *rdata;
213 size_t len;
214 char domainname_cstr[MAX_ESCAPED_DOMAIN_NAME];
215
216 // Receive and populate the cache with canned response
217 receive_response(req, msgptr, msgsz);
218
219 // Verify 2 cache entries for CName and A record are present
220 mDNSu32 CacheUsed =0, notUsed =0;
221 LogCacheRecords_ut(mDNS_TimeNow(m), &CacheUsed, &notUsed);
222 XCTAssertEqual(CacheUsed, m->rrcache_totalused);
223 XCTAssertEqual(CacheUsed, 4); // 2 for the CacheGroup object plus 2 for the A and CNAME records
224 XCTAssertEqual(m->PktNum, 1); // one packet was received
225
226 // Verify question's qname is now set with the A record's domainname
227 ConvertDomainNameToCString(&q->qname, domainname_cstr);
228 XCTAssertEqual(q->qnamehash, DomainNameHashValue(&q->qname));
229 XCTAssertFalse(strcmp(domainname_cstr, udns_cname_domainname_cstr));
230
231 // Verify client's add event for CNAME is properly formed
232 reply = req->replies;
233 XCTAssertNotEqual(reply, (reply_state*)mDNSNULL);
234 XCTAssertNil((__bridge id)reply->next);
235
236 data = (char *)&reply->rhdr[1];
237 end = data+reply->totallen;
238 get_string(&data, data+reply->totallen, name, kDNSServiceMaxDomainName);
239 rrtype = get_uint16(&data, end);
240 rrclass = get_uint16(&data, end);
241 rdlen = get_uint16(&data, end);
242 rdata = get_rdata(&data, end, rdlen);
243 len = get_reply_len(name, rdlen);
244
245 XCTAssertEqual(reply->totallen, len + sizeof(ipc_msg_hdr));
246 XCTAssertEqual(reply->mhdr->version, VERSION);
247 XCTAssertEqual(reply->mhdr->datalen, len);
248 XCTAssertEqual(reply->mhdr->ipc_flags, 0);
249 XCTAssertEqual(reply->mhdr->op, query_reply_op);
250
251 XCTAssertEqual(reply->rhdr->flags, htonl(kDNSServiceFlagsAdd));
252 XCTAssertEqual(reply->rhdr->ifi, kDNSServiceInterfaceIndexAny);
253 XCTAssertEqual(reply->rhdr->error, kDNSServiceErr_NoError);
254
255 XCTAssertEqual(rrtype, kDNSType_CNAME);
256 XCTAssertEqual(rrclass, kDNSClass_IN);
257 ConvertDomainNameToCString((const domainname *const)rdata, domainname_cstr);
258 XCTAssertFalse(strcmp(domainname_cstr, "test212.dotbennu.com."));
259
260 // The mDNS_Execute call generates an add event for the A record
261 m->NextScheduledEvent = mDNS_TimeNow_NoLock(m);
262 mDNS_Execute(m);
263
264 // Verify the client's reply contains a properly formed add event for the A record.
265 reply = req->replies;
266 XCTAssertNotEqual(reply, (reply_state*)mDNSNULL);
267 XCTAssertNotEqual(reply->next, (reply_state*)mDNSNULL);
268 reply = reply->next;
269
270 data = (char *)&reply->rhdr[1];
271 end = data+reply->totallen;
272 get_string(&data, data+reply->totallen, name, kDNSServiceMaxDomainName);
273 rrtype = get_uint16(&data, end);
274 rrclass = get_uint16(&data, end);
275 rdlen = get_uint16(&data, end);
276 rdata = get_rdata(&data, end, rdlen);
277 len = get_reply_len(name, rdlen);
278
279 XCTAssertEqual(reply->totallen, len + sizeof(ipc_msg_hdr));
280 XCTAssertEqual(reply->mhdr->version, VERSION);
281 XCTAssertEqual(reply->mhdr->datalen, len);
282
283 XCTAssertEqual(reply->mhdr->ipc_flags, 0);
284 XCTAssertEqual(reply->mhdr->op, query_reply_op);
285
286 XCTAssertEqual(reply->rhdr->flags, htonl(kDNSServiceFlagsAdd));
287 XCTAssertEqual(reply->rhdr->ifi, kDNSServiceInterfaceIndexAny);
288 XCTAssertEqual(reply->rhdr->error, kDNSServiceErr_NoError);
289
290 XCTAssertEqual(rrtype, kDNSType_A);
291 XCTAssertEqual(rrclass, kDNSClass_IN);
292 XCTAssertEqual(rdata[0], dns_response_ipv4.b[0]);
293 XCTAssertEqual(rdata[1], dns_response_ipv4.b[1]);
294 XCTAssertEqual(rdata[2], dns_response_ipv4.b[2]);
295 XCTAssertEqual(rdata[3], dns_response_ipv4.b[3]);
296 }
297
298 // This function verifies the cache and event handling occurred as expected when a network change happened.
299 // The uDNS_SetupDNSConfig is called to simulate a network change and two outcomes occur. First the A record
300 // query is restarted and sent to a new DNS server. Second the cache records are purged. Then mDNS_Execute
301 // is called and it removes the purged cache records and generates a remove event for the A record.
302 // The following are verified:
303 // 1.) The restart of query for A record.
304 // 2.) The cache is empty after mDNS_Execute removes the cache entres.
305 // 3.) The remove event is verified by examining the request's reply data.
306 - (void)_simulateNetworkChangeAndVerify
307 {
308 mDNS *const m = &mDNSStorage;
309 request_state* req = client_request_message;
310 DNSQuestion* q = &req->u.queryrecord.op.q;
311 mDNSu32 CacheUsed =0, notUsed =0;
312 const char *data; const char *end;
313 char name[kDNSServiceMaxDomainName];
314 uint16_t rrtype, rrclass, rdlen;
315 const char *rdata;
316 size_t len;
317
318 // The uDNS_SetupDNSConfig reconfigures the resolvers so the A record query is restarted and
319 // both the CNAME and A record are purged.
320 force_uDNS_SetupDNSConfig_ut(m);
321
322 // Verify the A record query was restarted. This is done indirectly by noticing the transaction id and interval have changed.
323 XCTAssertEqual(q->ThisQInterval, InitialQuestionInterval);
324 XCTAssertNotEqual(q->TargetQID.NotAnInteger, uDNS_TargetQID);
325
326 // Then mDNS_Execute removes both records from the cache and calls the client back with a remove event for A record.
327 m->NextScheduledEvent = mDNS_TimeNow_NoLock(m);
328 mDNS_Execute(m);
329
330 // Verify the cache entries are removed
331 LogCacheRecords_ut(mDNS_TimeNow(m), &CacheUsed, &notUsed);
332 XCTAssertEqual(CacheUsed, m->rrcache_totalused);
333 XCTAssertEqual(CacheUsed, 0);
334
335 // Verify the A record's remove event is setup as expected in the reply data
336 struct reply_state *reply;
337 reply = req->replies;
338 XCTAssertNotEqual(reply, (reply_state*)mDNSNULL);
339 XCTAssertNotEqual(reply->next, (reply_state*)mDNSNULL);
340 XCTAssertNotEqual(reply->next->next, (reply_state*)mDNSNULL);
341
342 reply = reply->next->next; // Get to last event to verify remove event
343 data = (char *)&reply->rhdr[1];
344 end = data+reply->totallen;
345 get_string(&data, data+reply->totallen, name, kDNSServiceMaxDomainName);
346 rrtype = get_uint16(&data, end);
347 rrclass = get_uint16(&data, end);
348 rdlen = get_uint16(&data, end);
349 rdata = get_rdata(&data, end, rdlen);
350 len = get_reply_len(name, rdlen);
351
352 XCTAssertEqual(reply->totallen, reply->mhdr->datalen + sizeof(ipc_msg_hdr));
353 XCTAssertEqual(reply->mhdr->version, VERSION);
354 XCTAssertEqual(reply->mhdr->datalen, len);
355 XCTAssertEqual(reply->mhdr->ipc_flags, 0);
356 XCTAssertEqual(reply->mhdr->op, query_reply_op);
357
358 XCTAssertNotEqual(reply->rhdr->flags, htonl(kDNSServiceFlagsAdd));
359 XCTAssertEqual(reply->rhdr->ifi, kDNSServiceInterfaceIndexAny);
360 XCTAssertEqual(reply->rhdr->error, kDNSServiceErr_NoError);
361
362 XCTAssertEqual(rrtype, kDNSType_A);
363 XCTAssertEqual(rrclass, kDNSClass_IN);
364 XCTAssertEqual(rdata[0], dns_response_ipv4.b[0]);
365 XCTAssertEqual(rdata[1], dns_response_ipv4.b[1]);
366 XCTAssertEqual(rdata[2], dns_response_ipv4.b[2]);
367 XCTAssertEqual(rdata[3], dns_response_ipv4.b[3]);
368 }
369
370 @end